package com.sqi.reactive.common.service

import com.sqi.reactive.common.repository.DynamicQueryRepository
import com.sqi.reactive.common.result.PageResult
import org.slf4j.Logger
import org.slf4j.LoggerFactory
import org.springframework.beans.factory.InitializingBean
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.core.ResolvableType
import org.springframework.data.annotation.Id
import org.springframework.data.domain.Pageable
import org.springframework.data.relational.core.query.Query
import org.springframework.util.ReflectionUtils
import reactor.core.publisher.Flux
import reactor.core.publisher.Mono
import java.io.Serializable
import java.lang.reflect.Field


/**
 * @author sjl
 * @date 2020/6/1
 */
@Suppress("SpringJavaInjectionPointsAutowiringInspection", "SpringJavaAutowiredMembersInspection")
abstract class BaseService<T, REPOSITORY : DynamicQueryRepository<T, ID>, ID : Serializable> : InitializingBean {
    @Autowired
    lateinit var repository: REPOSITORY
    private lateinit var tClass: Class<*>
    private lateinit var pkFieldName: String
    private lateinit var pkFieldType: Class<*>
    private lateinit var pkField: Field
    protected var log: Logger = LoggerFactory.getLogger(this.javaClass)

    override fun afterPropertiesSet() {
        val rt = ResolvableType.forClass(this.javaClass).superType
        tClass = rt.getGeneric(0).resolve() as Class<*>
        for (field in tClass.declaredFields) {
            val annotation = field.getAnnotation(Id::class.java)
            if (annotation != null) {
                pkFieldName = field.name
                pkFieldType = field.type
                pkField = field
                ReflectionUtils.makeAccessible(pkField)
                break
            }
        }
    }

    protected open fun T.check(message: String, block: (t: T) -> Boolean): T {
        if (!block(this)) {
            throw IllegalArgumentException("$message Object[${this.toString()}]")
        }
        return this
    }

    @Suppress("UNCHECKED_CAST")
    protected open fun T.getId(): ID? =
            ReflectionUtils.getField(pkField, this) as ID?

    protected open fun T.setId(id: ID): T {
        ReflectionUtils.setField(pkField, this, id)
        return this
    }

    protected open fun ID?.check(message: String, block: (id: ID?) -> Boolean): ID {
        if (!block(this)) {
            throw IllegalArgumentException(message)
        }
        return this!!
    }

    protected open fun ID?.checkNotNull(): ID = this.check("Id cannot be Null ") {
        this.isNotNull()
    }

    protected open fun ID?.isNull() = this == null
    protected open fun ID?.isNotNull() = this != null

    /**
     * 保存实体
     * @param entity
     */
    open fun save(entity: T): Mono<T> =
            entity.check("Entity.[id] must be null in save") {
                it.getId().isNull()
            }.let(this.repository::save)

    /**
     * 修改实体
     * @param entity
     */
    open fun update(entity: T, id: ID): Mono<T> = entity.setId(id).let(this.repository::save)


    /**
     * 根据主键删除
     * @param id
     */
    open fun deleteById(id: ID): Mono<Void> =
            id.checkNotNull().let(
                    this.repository::deleteById
            )

    /**
     * 删除实体
     */
    open fun delete(entity: T): Mono<Void> =
            entity.check("Id cannot be null in delete") {
                it.getId().isNotNull()
            }.let(this.repository::delete)

    /**
     *  根据主键查询
     *  @param id
     */
    open fun findById(id: ID): Mono<T> =
            id.checkNotNull().let {
                this.repository.findById(id)
            }

    /**
     * 根据条件查询所有
     *
     * @param params 查询条件
     */
    open fun findAll(params: Map<String, Any>?): Flux<T> = this.repository.findAll(params)

    /**
     * 根据条件查询所有,带分页
     * @param params 查询条件
     * @param pageable 分页条件
     */
    open fun findAll(params: Map<String, Any>?, pageable: Pageable): Mono<PageResult<T>> {
        return this.repository.findAll(params, pageable)
    }

    /**
     * 查询所有
     *
     * @param query
     */
    open fun findAll(query: Query): Flux<T> = this.repository.findAll(query)

    /**
     * 分页查询
     * @param query
     * @param pageable
     */
    open fun findAll(query: Query, pageable: Pageable): Mono<PageResult<T>> = this.repository.findAll(query, pageable)
//
    /**
     * 返回元数据描述
     */
    open fun metaInfo(): Mono<Map<String, Class<*>>> = this.repository.metaInfo()

}